/*
 * CPU.java
 *
 * Created on 2 de Abril de 2008, 01:27
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package Simulador.Hardware;

import Auxiliar.Fila;
import Simulador.Event.Evento;
import Simulador.Job.Segmento;
import Simulador.simulador;

/**
 * Implementa a CPU e a fila de CPU
 * @author Fernando/Matheus
 */
public class CPU {

    boolean busy;
    private static final float TEMPO_TROCA_SEGMENTO = .1f;
    private static final float TEMPO_EXECUTA_SEGMENTO = 0.01f;
    private static final float TEMPO_ADICIONA_SEGMENTO = 0.02f;
    private Fila segExecutando;
    private Evento proxEvento;
    private Segmento segAtual;

    /** Creates a new instance of CPU */
    public CPU() {
        busy = false;
        segExecutando = new Fila();
    }

    /**
     * Adiciona Segmento a fila de segmentos que estao sendo executados.
     * @param s
     * Segmento a ser adicionado.
     * @return
     * Tempo gasto na operacao.
     */
    public float adicionarSegmento(Segmento s) {
        if (segAtual == null) {
            segAtual = s;
            return TEMPO_EXECUTA_SEGMENTO;
        } else {
            segExecutando.addItem(s);
            return TEMPO_ADICIONA_SEGMENTO;
        }
    }

    /**
     * 
     * @return
     * true caso CPU esteja ocupada
     */
    public boolean isBusy() {
        return busy;
    }

    /**
     * Pausa o segmento
     * Equivalente a guardar o estado do segmento para tratar uma interrupcao
     * ou para executar outro segmento.
     * 
     * @param tempoAtual
     * Momento atual da simulacao.
     */
    public void pausarSegmento(float tempoAtual) {
        if (segAtual != null) {
            segAtual.pausaSegmento(tempoAtual);
            segAtual.setProxEvento(proxEvento);
            proxEvento = null;
            busy = false;
            simulador.printEvento("Segmento pausado:" + segAtual.getId(), simulador.DEBUG_CPU, simulador.prioridadeDebug1);
        }
    }
    
    /**
     * Restaura estado de um segmento pausado anteriormente.
     * @param tempoAtual
     * Momento atual da simulacao.
     * @param tempoMaximo
     * Momento em que o segmento vai ser tirado da CPU.
     */
    public void continuarSegmento(float tempoAtual, float tempoMaximo) {
        if (segAtual != null) {
            segAtual.recomecaSegmento(tempoAtual);
            if(proxEvento != null){
                throw new Error("proxEvento deveria estar vazio.");
            }
            proxEvento = segAtual.proximoEvento(tempoAtual, tempoMaximo);
            busy = true;
            simulador.printEvento("Segmento continuado:" + segAtual.getId(), simulador.DEBUG_CPU, simulador.prioridadeDebug1);
        }
    }
    
    /**
     * Pausa segmento atual e continua outro que estiver na fila.
     * Pode ser inclusive o mesmo segmento(caso só exista um segmento sendo executado).
     * @param tempoAtual
     * Momento atual da simulacao.
     * @param tempoMaximo
     * Momento em que o processo vai ser tirado da CPU.
     * @return
     * Tempo necessario para executar a operacao.
     */
    public float executarProximoSegmento(float tempoAtual, float tempoMaximo) {
        if(segAtual != null){
            segExecutando.addItem(segAtual);
        }
        segAtual = (Segmento) segExecutando.getItem();
        simulador.printEvento(" CPU: IdProcesso: "+ segAtual.getIdProcesso() + " IdSegmento: " + segAtual.getId(), Evento.ProxSegmento ,simulador.prioridadeSaida);
        return TEMPO_TROCA_SEGMENTO;
    }
    
    /**
     * Tira processo atual da fila de execucao
     * @return
     * processo removido.
     */
    public Segmento finalizarSegmento() {
        Segmento p = segAtual;
        segAtual = null;
        return p;
    }

    
    /**
     * Proximo evento causado pelo segmento em execucao.
     * @param tempoMaximo
     * Momento em que o segmento vai ser tirado da CPU.
     * @return
     * Evento causado pelo segmento atual.
     */
    public Evento getProximoEvento(int tempoMaximo) {
        if (proxEvento.getMomentoDeChegada() <= tempoMaximo) {
            Evento e = proxEvento;
            proxEvento = null;
            return e;
        } else {
            return null;
        }
    }
}